/******************************************************************************
 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************/
#include "s7816.h"
#include "dma.h"
#include "plic.h"

volatile unsigned int s7816_rst_pin;
volatile unsigned int s7816_vcc_pin;
volatile unsigned int s7816_rtx_pin;
volatile unsigned char s7816_clock;
volatile int s7816_rst_time;  // us
volatile int s7816_atr_time;  // us
/**
 * @brief      	This function is used to set the s7816 clock.
 * @param[in]  	div	- set the divider of clock of 7816 module.
 * @return     	none.
 * @note        system clk is 24MHZ
 * 				7816clk:    0x60-4Mhz     0x40-6Mhz   0x20-12Mhz
 * 				baudrate:   0x60-10752    0x40-16194  0x20-32388
 * 				the clk-pin is PA0 by default.
 */
void s7816_set_clk(unsigned char div)
{
    reg_7816_clk_div &= 0x0f;
    reg_7816_clk_div |= (unsigned char)div;
}

/**
 * @brief      	This function is used to set the rst-wait time of the s7816 module.
 * @param[in]  	rst_time_us - set the s7816_rst_time.
 * @param[in]  	atr_time_us - set the s7816_atr_time.
 * @return     	none.
 */
void s7816_set_time(int rst_time_us, int atr_time_us)
{
    s7816_rst_time = rst_time_us;
    s7816_atr_time = atr_time_us;
}
/**
 * @brief      	This function is used to set the RST pin of s7816.
 * @param[in]  	pin_7816_rst - the RST pin of s7816.
 * @return     	none.
 */
void s7816_set_rst_pin(gpio_pin_e pin_7816_rst)
{
    s7816_rst_pin = pin_7816_rst;
    gpio_function_en(pin_7816_rst);
    gpio_output_en(pin_7816_rst);
    gpio_input_dis(pin_7816_rst);
    gpio_set_low_level(pin_7816_rst);
}

/**
 * @brief      	This function is used to set the VCC pin of s7816.
 * @param[in]   pin_7816_vcc - the VCC pin of s7816.
 * @return     	none.
 */
void s7816_set_vcc_pin(gpio_pin_e pin_7816_vcc)
{
    s7816_vcc_pin = pin_7816_vcc;
    gpio_function_en(pin_7816_vcc);
    gpio_output_en(pin_7816_vcc);
    gpio_input_dis(pin_7816_vcc);
    gpio_set_low_level(pin_7816_vcc);
}

/**
 * @brief      	This function is used to initialize the s7816 module.
 * @param[in]  	uart_num     - UART0 or UART1.
 * @param[in]  	clock        - the clock of s7816.
 * @param[in]  	f            - the clock frequency regulator of s7816,372 by default.
 * @param[in]  	d            - the bitrate regulator of s7816,1 by default.
 * @return     	none.
 */
void s7816_init(uart_num_e uart_num, s7816_clock_e clock, int f, int d)
{
    unsigned short div;
    unsigned char bwpc;
    s7816_clock = clock;
    s7816_rst_time = 40000 / clock;  // us
    s7816_atr_time = 40000 / clock;  // us

    int baud = clock * 1000000 * d / f;
    if (clock == S7816_4MHZ) {
        s7816_set_clk(0x60);
    } else if (clock == S7816_6MHZ) {
        s7816_set_clk(0x40);
    } else if (clock == S7816_12MHZ) {
        s7816_set_clk(0x20);
    }
    uart_reset(uart_num);
    uart_cal_div_and_bwpc(baud, 24 * 1000 * 1000, &div, &bwpc);
    uart_init(uart_num, div, bwpc, UART_PARITY_EVEN,
              UART_STOP_BIT_ONE);  // 7816 protocol stipulate the parity bit should be even.
}

/**
 * @brief      	This function is used to set all the pin of s7816 module.
 * @param[in]  	rst_pin     - the rst pin of s7816.
 * @param[in]  	vcc_pin     - the vcc pin of s7816.
 * @param[in]  	clk_pin     - the clk pin of s7816.
 * @param[in]  	trx_pin     - the trx pin of s7816.
 * @return     	none.
 */
void s7816_set_pin(gpio_pin_e rst_pin, gpio_pin_e vcc_pin, s7816_clk_pin_e clk_pin, s7816_rtx_pin_e rtx_pin)
{
    s7816_set_rst_pin(rst_pin);
    s7816_rst_pin = rst_pin;

    s7816_set_vcc_pin(vcc_pin);
    s7816_vcc_pin = vcc_pin;

    reg_gpio_func_mux(clk_pin) = (reg_gpio_func_mux(clk_pin) & (~BIT_RNG(0, 1))) | BIT(0);
    gpio_function_dis(clk_pin);

    s7816_rtx_pin = rtx_pin;  // if the trx function set to early,it may trigger interrupt by accident.
                              // so we set the function in coldreset.
}

/**
 * @brief      	This function is used to active the IC card,set the trx pin and coldreset.
 * @param[in]  	none.
 * @return     	none.
 */
void s7816_coldreset(void)
{
    gpio_set_high_level(s7816_vcc_pin);
    delay_us(20);                //  wait for the vcc  stable.
    reg_7816_clk_div |= BIT(7);  //  enable the 7816 clk,the pin is A0.
    delay_us(s7816_rst_time);
    s7816_set_rtx_pin(s7816_rtx_pin);    // uart tx/rx pin set,if the trx pin set before this place,it may
    gpio_set_high_level(s7816_rst_pin);  // the IC card will return the initial ATR.
    delay_us(s7816_atr_time);
}

/**
 * @brief      	This function is used to release the trigger.
 * @param[in]  	none.
 * @return     	none.
 */
void s7816_release_trig(void)
{
    gpio_set_low_level(s7816_rst_pin);
    reg_7816_clk_div &= (BIT(7) - 1);
    gpio_set_low_level(s7816_vcc_pin);
}

/**
 * @brief      	This function is used to warmreset.
 * @param[in]  	none.
 * @return     	none.
 * @note        the warmreset is required after the IC-CARD active.
 */
void s7816_warmreset(void)
{
    gpio_set_low_level(s7816_rst_pin);
    delay_us(s7816_rst_time);
    gpio_set_high_level(s7816_rst_pin);  // The IC card will return the initial ATR.
    delay_us(s7816_atr_time);
}

/**
 * @brief      	This function is used to warmreset.
 * @param[in]  	uart_num - UART0 or UART1.
 * @param[in]   tx_data  - the data need to send.
 * return       none.
 */
void s7816_send_byte(uart_num_e uart_num, unsigned char tx_data)
{
    uart_send_byte(uart_num, tx_data);
    uart_rtx_pin_tx_trig(uart_num);
}
